package com.dtflys.easyel.compile;

import com.dtflys.easyel.antlr.EasyElLexer;
import com.dtflys.easyel.antlr.EasyElParser;
import com.dtflys.easyel.ast.ASTNode;
import com.dtflys.easyel.ast.ASTTransformVisitor;
import com.dtflys.easyel.exception.EasyElException;
import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CommonTokenStream;

import java.io.IOException;

/**
 * @author gongjun[jun.gong@thebeastshop.com]
 * @since v1.0.0
 */
public class EasyElCompiler {

    private final EasyElSource source;

    private EasyElCompileConfiguration compileConfiguration;

    public EasyElCompiler(EasyElSource source) {
        this(source, null);
    }

    public EasyElCompiler(EasyElSource source, EasyElCompileConfiguration compileConfiguration) {
        this.source = source;
        this.compileConfiguration = compileConfiguration;
    }


    public EasyElCompiledResult compile() {
        ASTNode node = compileToAST();
        return new EasyElCompiledResult(source, node);
    }


    private ASTNode compileToAST() {
        ANTLRInputStream charStream = null;
        try {
            charStream = new ANTLRInputStream(source.getInputStream());
        } catch (IOException e) {
            throw new EasyElException("Failed to initialize input stream: ", e);
        }
        return compileToAST(charStream);
    }

    private ASTNode compileToAST(CharStream inputStream) {
        EasyElErrorListener errorListener = new EasyElErrorListener(
                source, compileConfiguration.getMaxParseErrorNumber());
        EasyElLexer lexer = new EasyElLexer(inputStream);
        lexer.removeErrorListeners();
        lexer.addErrorListener(errorListener);
        CommonTokenStream tokenStream = new CommonTokenStream(lexer);
        EasyElParser parser = new EasyElParser(tokenStream);
        parser.removeErrorListeners();
        parser.addErrorListener(errorListener);
        EasyElParser.CompilationUnitContext compilationUnitContext = parser.compilationUnit();
        ASTTransformVisitor transformVisitor = new ASTTransformVisitor(source, compileConfiguration, errorListener);
        errorListener.interruptIfHasError();
        ASTNode node = transformVisitor.visitCompilationUnit(compilationUnitContext);
        errorListener.interruptIfHasError();
        return node;
    }

}
